<?php

declare(strict_types=1);

namespace App\Resource\Service;

use App\Common\Constants\ErrorCode;
use App\Common\Service\BaseService;
use App\Resource\Model\CrdNavigationModel;
use App\Resource\Model\CrdNavigationShopModel;
use App\Resource\Model\GoodsModel;
use App\Resource\Model\JumpTypeModel;
use App\Resource\Model\NavigationModel;
use App\Resource\Model\NavigationShopModel;
use Exception;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;

class NavigationService extends BaseService
{
    /**
     * @Inject
     * @var ResourceService
     */
    protected $resourceService;

    /**
     * @param array $where
     * @param int $perPage
     * @param int $type 1图文导航，2轮播图，3弹窗广告，4广告,5落地页
     * @param array|string[] $field
     *
     * @return array
     */
    public function getList(array $where, int $perPage = 15, int $type, array $field = ['*'])
    {
        $shopType = $where['shop_type'] ?? null;
        if($shopType){
            // 次日达商城
            $query = CrdNavigationModel::query();
        }else{
            // 普通商城
            $query = NavigationModel::query();
        }

        !empty($where['title'])
        && $query->whereRaw('INSTR(title, ?) > 0', [$where['title']]);
        !empty($where['start_time'])
        && $query->whereDate('created_at', '>=', $where['start_time']);
        !empty($where['end_time'])
        && $query->whereDate('created_at', '<=', $where['end_time']);
        $query->where('type', '=', $type);
        $list = $query->latest('sort')->paginate($perPage, $field);
        if (!in_array($type, [4, 5])) {
            foreach ($list as $key => $val) {
                if ($type == 1) {
                    //图文导航，若jump_type = 5,商品数量为jump_url所选分类下的商品数.jump_type = 6，商品数量为1
                    if ($val['jump_type'] == 5) {
                        $list[$key]['goods_count'] = GoodsModel::query()->where(['cate_id' => $val['jump_url']])->count();
                    } elseif ($val['jump_type'] == 6) {
                        $list[$key]['goods_count'] = 1;
                    } else {
                        $list[$key]['goods_count'] = '-';
                    }
                } else {
                    if($shopType){
                        //次日达商城
                        $list[$key]['shop_count'] = CrdNavigationShopModel::where('navigation_id', '=', $val->id)->count();
                    }else{
                        // 普通商城
                        $list[$key]['shop_count'] = NavigationShopModel::where('navigation_id', '=', $val->id)->count();
                    }
                }
            }
        }
        return ['code' => ErrorCode::SUCCESS, 'data' => $list];
    }

    /**
     * @param int $id
     * @param int $type
     * @param array|string[] $field
     *
     * @return array
     */
    public function getInfoById(int $id, int $type, array $field = ['*'], $shopType = null)
    {
        if($shopType){
            // 次日达商城
            $res = CrdNavigationModel::query()->where(['id' => $id, 'type' => $type])->select($field)->first();
        }else{
            // 普通商城
            $res = NavigationModel::query()->where(['id' => $id, 'type' => $type])->select($field)->first();
        }
        if (!$res) {
            return ['code' => ErrorCode::NOT_EXIST];
        }

        return ['code' => ErrorCode::SUCCESS, 'data' => $res];
    }

    /**
     * @param array $params
     * @param int $type     1图文导航，2轮播图，3弹窗广告，4公告管理，5 落地页 6 优惠券
     *
     * @return array
     */
    public function add(array $params, int $type)
    {
        //跳转组件验证,若有二级则不能为空（数据库表 is_two_level 字段不为 0）
        if (in_array($params['jump_type'], [5, 6, 9, 10, 12, 14, 16, 17, 18, 19]) && !$params['jump_url']) {
            return ['code' => ErrorCode::SYSTEM_INVALID];
        }
        $params['type'] = $type;

        $shopType = $params['shop_type'] ?? null;
        if($shopType){
            // 次日达商城
            $res = CrdNavigationModel::create($params);
        }else{
            // 普通商城
            $res = NavigationModel::create($params);
        }

        if (!$res) {
            return ['code' => ErrorCode::NOT_IN_FORCE];
        }
        //绑定门店，图文导航、公告管理、落地页可以绑定店铺
        if (!in_array($type, [1, 4, 5]) && !empty($params['shop_ids'])) {
            $this->resourceService->bindShop('navigation', $res->id, $params['shop_ids'], $type, $shopType);
        }
        $this->batchUpdateNavForRedis("{$type}-{$shopType}", explode(',', $params['shop_ids'] ?? ''));    //测试期间暂时注释掉，测试完成需打开
        return ['code' => ErrorCode::SUCCESS, 'data' => [], 'info' => ['target_id' => $res -> id]];
    }

    /**
     * @param array $params
     * @param int $type
     *
     * @return array
     */
    public function update(array $params, int $type)
    {
        if (!in_array($type, [4, 5])) {
            $shopIds = isset($params['shop_ids']) ? $params['shop_ids'] : null;
            unset($params['shop_ids']);
        }
        $shopType = $params['shop_type'] ?? null;
        unset($params['shop_type']);
        if($shopType){
            //次日达商城
            $res = CrdNavigationModel::where(['id' => $params['id'], 'type' => $type])->update($params);
        }else{
            //普通商城
            $res = NavigationModel::where(['id' => $params['id'], 'type' => $type])->update($params);
        }
        if (!$res) {
            return ['code' => ErrorCode::NOT_IN_FORCE];
        }
        $this->batchUpdateNavForRedis("{$type}-{$shopType}", $this->getUseStoreIds($params['id'])->toArray());
        //绑定门店
        if (!in_array($type, [1, 4, 5]) && !empty($shopIds)) {
            $this->resourceService->bindShop('navigation', (int)$params['id'], $shopIds, $type, $shopType);
        }
        return ['code' => ErrorCode::SUCCESS, 'data' => $res, 'info' => ['target_id' => $params['id']]];
    }

    /**
     * 获取正在应用的门店id
     *
     * @param $id
     * @return \Hyperf\Utils\Collection
     */
    public function getUseStoreIds($id)
    {
        return NavigationShopModel::query()->where('navigation_id', $id)->pluck('shop_id');
    }

    /**
     * @param int $ids
     * @param int $type
     * @param false|int $shopType
     * @return array
     */
    public function delete(int $ids, int $type, $shopType = false)
    {
        try {
            $this->batchUpdateNavForRedis("{$type}-{$shopType}", $this->getUseStoreIds($ids)->toArray());
            DB::transaction(function () use ($ids, $type, $shopType) {
                if($shopType){
                    // 次日达商城
                    $res = CrdNavigationModel::where(['id' => $ids, 'type' => $type])->delete();
                    CrdNavigationShopModel::where(['navigation_id' => $ids, 'type' => $type])->delete();
                }else{
                    // 普通商城
                    $res = NavigationModel::where(['id' => $ids, 'type' => $type])->delete();
                    NavigationShopModel::where(['navigation_id' => $ids, 'type' => $type])->delete();
                }
                if (!$res) {
                    throw new \Exception('删除失败', ErrorCode::NOT_IN_FORCE);
                }
            });
        } catch (Exception $e) {
            return ['code' => ErrorCode::NOT_IN_FORCE];
        }
        return ['code' => ErrorCode::SUCCESS, 'data' => [], 'info' => ['target_id' => $ids]];
    }

    /**
     * @return array
     */
    public function getJumpList()
    {
        $res = JumpTypeModel::all();
        return ['code' => ErrorCode::SUCCESS, 'data' => $res];
    }

    /**
     * @param int $id
     * @param int $sort
     *
     * @return array
     */
    public function editSort(int $id, int $sort, $shopType = null)
    {
        if($shopType){
            //次日达
            $res = CrdNavigationModel::query()->where('id', $id)->update(['sort' => $sort]);
        }else{
            //普通商城
            $res = NavigationModel::query()->where('id', $id)->update(['sort' => $sort]);
        }
        if ($res) {
            return ['code' => ErrorCode::SUCCESS, 'data' => $res, 'info' => ['target_id' => $id]];
        }

        return ['code' => ErrorCode::NOT_IN_FORCE];
    }

    /**
     * 小程序获取首页导航数据
     *
     * @param $shop_id
     * @return array
     */
    public function getNavList2($shop_id = 0)
    {
        $result = [];
        $where  = function ($q) use ($shop_id) {
            $nav_ids = $this->getShopNavigationIds($shop_id);
            $q->whereIn('id', $nav_ids);
            $q->where('start_time', '<', date('Y-m-d H:i:s'));
            $q->where('end_time', '>', date('Y-m-d H:i:s'));
        };
        // Data
        $result['nav_list']     = $this->getNavForRedis(NavigationModel::TYPE_NAV, 'nav_list');
        $result['banner_list']  = $shop_id ? $this->getNavForRedis(NavigationModel::TYPE_PLAY_IMAGE, $shop_id, 'banner_list', $where) : [];
        $result['popup_list']   = $shop_id ? $this->getNavForRedis(NavigationModel::TYPE_POPUP, $shop_id, 'popup_list', $where) : [];
        $result['notice_list']  = $this->getNavForRedis(NavigationModel::TYPE_NOTICE, 'notice_list', 'resource', ['status' => 1]);
        $result['coupon_image'] = $this->getNavForRedis(NavigationModel::TYPE_COUPON, 'coupon_list');
        return $result;
    }

    /**
     * 从 mysql 获取导航数据
     *
     * @param $type
     * @param $filed
     * @param array $whereCallback
     * @return array
     */
    public function getNavForMysql($type, $filed, $whereCallback = [])
    {
        return NavigationModel::query()
            ->select($filed)
            ->where('type', $type)
            ->where($whereCallback)
            ->orderBy('sort','desc')
            ->limit(10)
            ->get()
            ->toArray();
    }

    /**
     * 从 redis 获取导航数据
     *
     * @param $type
     * @param $hashKey
     * @param string $key
     * @param array $whereCallback
     * @return array
     */
    public function getNavForRedis($type, $hashKey, $key = 'resource', $whereCallback = [])
    {
        $hashKey = (string)$hashKey;
        if (!$this->resourceRedis->hExists($key, $hashKey)) {
            $filed = ['id', 'title', 'img', 'created_at', 'jump_type', 'jump_url', 'sort', 'end_time'];
            $list  = $this->getNavForMysql($type, $filed, $whereCallback);
            // 如果数据库里面取不到数据就不进入 redis，否则存入空数据
            if (!$list) {
                return [];
            }
            $this->resourceRedis->hSet($key, $hashKey, json_encode($list));
            // 过滤掉未设置过期时间的
            if ($list = array_filter(array_column($list, 'end_time'))) {
                $ttl = strtotime(min($list)) - time();
                $ttl > 0 && $this->resourceRedis->expire($key, $ttl);
            }
        }
        $result = $this->resourceRedis->hGet($key, $hashKey) ?? '{}';
        return json_decode($result, true);
    }



    /**
     * 根据 navigation 类型获取 Redis Key
     *
     * @param $type
     * @param null $shop_id
     * @return array
     */
    public function getRedisHashKey($type, $shop_id = null): array
    {
        $key = 'resource';
        $hashKey = null;
        switch ($type) {
            case NavigationModel::TYPE_NAV:
                $hashKey = 'nav_list';
                break;
            case NavigationModel::TYPE_PLAY_IMAGE:
                $key = 'banner_list';
                $hashKey = $shop_id;
                break;
            case NavigationModel::TYPE_POPUP:
                $key = 'popup_list';
                $hashKey = $shop_id;
                break;
            case NavigationModel::TYPE_NOTICE:
                $hashKey = 'notice_list';
                break;
            case NavigationModel::TYPE_LANDING:
                $hashKey = 'landing_list';
                break;
            case NavigationModel::TYPE_COUPON:
                $hashKey = 'coupon_list';
                break;
        }
        return [
            'key'     => $key,
            'hashKey' => $hashKey,
        ];
    }

    /**
     * 获取指定商家的导航id数组
     *
     * @param $shop_id
     * @param $isNextDay
     * @return array
     */
    public function getShopNavigationIds($shop_id, $isNextDay = false): array
    {
        $model = $isNextDay ? CrdNavigationShopModel::query() : NavigationShopModel::query();
        $ids = $model->where('shop_id', $shop_id)->pluck('navigation_id');
        return $ids ? $ids->toArray() : [];
    }

    /**
     * 获取 hash 值
     *
     * @param $func
     * @param string $hashKey
     * @param string $key
     * @param $shopType
     * @return array|mixed
     */
    public function hashGet($func, string $hashKey, string $key = 'resource', $shopType = 0)
    {
        if ($data = $this->hGetToArray($key, $hashKey)) {
            return $data;
        }
        $list = $func($shopType === 1 ? CrdNavigationModel::query() : NavigationModel::query())->get();
        if (!count($list)) {
            return [];
        }
        $this->resourceRedis->hSet($key, $hashKey, $list->toJson());
        $this->setExpireByTime($list, $key);
        return $this->hGetToArray($key, $hashKey);
    }

    /**
     * 通过最小的过期时间设置过期
     *
     * @param $list
     * @param $key
     */
    public function setExpireByTime($list, $key)
    {
        // 过滤掉未设置过期时间的
        if ($list = array_filter(array_column($list->toArray(), 'end_time'))) {
            $ttl = strtotime(min($list));
            $ttl > time() && $this->resourceRedis->expireAt($key, $ttl);
        }
    }

    /**
     * 转换 hash 类型
     *
     * @param $key
     * @param $hashKey
     * @return mixed
     */
    public function hGetToArray($key, $hashKey)
    {
        $result = $this->resourceRedis->hGet($key, $hashKey);
        !$result && $result = '{}';
        return json_decode($result, true);
    }

    /**
     * 批量更新到 redis
     *
     * @param $type
     * @param array $shop_ids
     */
    public function batchUpdateNavForRedis($type, array $shop_ids = [])
    {
        $types = explode('-', $type);
        empty($shop_ids) && array_push($shop_ids, null);
        foreach ($shop_ids as $shop_id) {
            $hashKey = $this->getHashKeys($type, $shop_id);
            $this->resourceRedis->hDel($hashKey['key'], $hashKey['hashKey']);
        }
        $this->getNavList(0, boolval($types[1] ?? false) ? 1 : 0);
    }

    /**
     * 根据 type 获取 hashKey
     *
     * @param $type
     * @param null $shop_id
     * @return array
     */
    public function getHashKeys($type, $shop_id = null)
    {
        $types = explode('-', $type);
        $isNext = boolval($types[1] ?? false);
        $prefix = $isNext ? 'crd' : 'jsd';
        return [
            'key'     => "{$prefix}:nav:{$types[0]}",
            'hashKey' => $shop_id ? $shop_id : 'list',
        ];
    }

    /**
     * 小程序获取首页导航数据
     *
     * @param $shop_id
     * @param int $shopType [商城类型 0：普通商城，1：次日达商城首页]
     * @return array
     */
    public function getNavList($shop_id = 0, $shopType = 0)
    {
        $result = [];
        $resultKey = $this->getKeysFunc($shop_id);
        $needBindShop = [NavigationModel::TYPE_POPUP, NavigationModel::TYPE_PLAY_IMAGE];
        $prefix = $shopType == 1 ? 'crd' : 'jsd';
        for ($i = 1; $i < 7; $i++) {
            if ($resultKey[$i]) {
                $key = "{$prefix}:nav:{$i}";
                $hashKey = !in_array($i, $needBindShop) ? 'list' : (string)$shop_id;
                $result[$resultKey[$i]['key']] = $this->hashGet(function ($model) use ($i, $resultKey) {
                    return $resultKey[$i]['callback']($model, $i);
                }, $hashKey, $key, $shopType);
            }
        }
        return $result;
    }

    /**
     * 获取首页的 redis 数据集合的 key 和查询条件等
     *
     *
     * @param $shop_id
     * @return array
     */
    public function getKeysFunc($shop_id)
    {
        $where = function ($q) use ($shop_id) {
            $nav_ids = $this->getShopNavigationIds($shop_id);
            $q->whereIn('id', $nav_ids);
            $q->where('start_time', '<', date('Y-m-d H:i:s'));
            $q->where('end_time', '>', date('Y-m-d H:i:s'));
        };
        return [
            '',
            [
                'key'      => 'nav_list',
                'callback' => function ($model, $typeIndex) {
                    $field = ['id', 'title', 'img', 'created_at', 'jump_type', 'jump_url', 'sort', 'end_time'];
                    return $model->select($field)->where('type', $typeIndex)->orderBy('sort', 'desc')->limit(10);
                },
            ],
            [
                'key'      => 'banner_list',
                'callback' => function ($model, $typeIndex) use ($where) {
                    $field = ['id', 'title', 'img', 'created_at', 'jump_type', 'jump_url', 'sort', 'end_time'];
                    return $model->select($field)
                        ->where($where)
                        ->where('type', $typeIndex)
                        ->orderBy('sort', 'desc')
                        ->limit(10);
                },
            ],
            [
                'key'      => 'popup_list',
                'callback' => function ($model, $typeIndex) use ($where) {
                    $field = ['id', 'title', 'img', 'created_at', 'jump_type', 'jump_url', 'sort', 'end_time'];
                    return $model->select($field)
                        ->where($where)
                        ->where('type', $typeIndex)
                        ->orderBy('sort', 'desc')
                        ->limit(10);
                },
            ],
            [
                'key'      => 'notice_list',
                'callback' => function ($model, $typeIndex) {
                    $field = ['id', 'title', 'img', 'created_at', 'jump_type', 'jump_url', 'sort', 'end_time'];
                    return $model->select($field)
                        ->where('status', 1)
                        ->where('type', $typeIndex)
                        ->orderBy('sort', 'desc')
                        ->limit(10);
                },
            ],
            [
                'key'      => 'landing_list',
                'callback' => function ($model, $typeIndex) {
                    $field = ['id', 'title', 'img', 'created_at', 'jump_type', 'jump_url', 'sort', 'end_time'];
                    return $model->select($field)->where('type', $typeIndex)->orderBy('sort', 'desc')->limit(10);
                },
            ],
            [
                'key'      => 'coupon_image',
                'callback' => function ($model, $typeIndex) {
                    $field = ['id', 'title', 'img', 'created_at', 'jump_type', 'jump_url', 'sort', 'end_time'];
                    return $model->select($field)->where('type', $typeIndex)->orderBy('sort', 'desc')->limit(10);
                },
            ],
        ];
    }

    // 分类上面的优惠券图片
    public function couponPic(){
        $key = 'jsd:nav:6';
        $res = $this->resourceRedis->hGet($key, 'list');
        if (!$res){
            $field = ['id', 'title', 'img', 'created_at', 'jump_type', 'jump_url', 'sort', 'end_time'];
            $res = NavigationModel::query()
                ->select($field)
                ->where(['type' => NavigationModel::TYPE_COUPON])
                ->orderBy('sort', 'desc')
                ->first();
        }else{
            $arr = json_decode($res, true);
            $res = array_shift($arr);
        }
        return ['code' => 1, 'msg' => '获取成功', 'data' => $res];
    }

}
